package com.jing.yuntuku.service;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.jing.yuntuku.exception.BusinessException;
import com.jing.yuntuku.exception.ErrorCode;
import com.jing.yuntuku.model.dto.file.UploadPictureResult;
import com.jing.yuntuku.utils.AliOssUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;

/**
 * OSS图片上传抽象模板类（遵循PictureUploadTemplate风格）
 * 定义上传固定流程，差异化逻辑由子类实现
 */
@Setter
@Slf4j
@Component
public abstract class OssPictureUploadTemplate {

    // OSS工具类（封装配置，子类共享）
    @Resource
    protected AliOssUtil aliOssUtil;

    /**
     * 模板方法：上传图片完整流程（固定不变，子类无需重写）
     * @param inputSource 输入源（可传MultipartFile或URL字符串）
     * @param uploadPathPrefix OSS上传路径前缀（如"user-avatar/"）
     * @return 上传结果（含图片信息、访问URL）
     */
    public final UploadPictureResult uploadPicture(Object inputSource, String uploadPathPrefix) {
        // 1. 验证输入源有效性（差异化：子类实现）
        validPicture(inputSource);

        // 2. 生成唯一文件名（固定流程）
        String uuid = RandomUtil.randomString(16); // 16位随机字符串（替代UUID，保持风格一致）
        String originFilename = getOriginFilename(inputSource); // 提取原始文件名（差异化：子类实现）
        String fileSuffix = FileUtil.getSuffix(originFilename); // 获取文件扩展名
        String uploadFilename = String.format("%s_%s.%s", DateUtil.formatDate(new Date()), uuid, fileSuffix);
        String uploadPath = buildOssUploadPath(uploadPathPrefix, uploadFilename); // 构建OSS完整路径

        File tempFile = null;
        try {
            // 3. 创建临时文件（固定流程）
            tempFile = createTempFile(uploadPath);

            // 4. 处理文件（差异化：子类实现，如本地文件转临时文件、URL下载到临时文件）
            processFile(inputSource, tempFile);

            // 5. 上传文件到OSS（固定流程）
            PutObjectResult putObjectResult = uploadToOss(uploadPath, tempFile);

            // 6. 获取图片宽高信息（固定流程）
            BufferedImage imageInfo = getImageDimension(tempFile);

            // 7. 构建并返回上传结果（固定流程）
            return buildUploadResult(originFilename, tempFile, uploadPath, imageInfo);

        } catch (Exception e) {
            log.error("图片上传到OSS失败，上传路径：{}", uploadPath, e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
        } finally {
            // 8. 清理临时文件（固定流程）
            deleteTempFile(tempFile);
        }
    }


    // -------------------------- 固定流程：模板类实现 --------------------------
    /**
     * 1. 构建OSS完整上传路径（前缀 + 唯一文件名）
     */
    private String buildOssUploadPath(String uploadPathPrefix, String uploadFilename) {
        // 处理前缀斜杠，避免重复或缺失（如"user-avatar"→"user-avatar/"）
        String normalizedPrefix = StringUtils.hasText(uploadPathPrefix)
                ? uploadPathPrefix.endsWith("/") ? uploadPathPrefix : uploadPathPrefix + "/"
                : "";
        return normalizedPrefix + uploadFilename;
    }

    /**
     * 2. 创建临时文件（用于存储待上传的文件内容）
     */
    private File createTempFile(String uploadPath) throws IOException {
        // 临时文件前缀用上传路径的关键部分，后缀用文件扩展名（保持与PictureUploadTemplate风格一致）
        String tempPrefix = FileUtil.mainName(uploadPath) + "_temp_";
        String tempSuffix = "." + FileUtil.getSuffix(uploadPath);
        File tempFile = File.createTempFile(tempPrefix, tempSuffix);
        // 设置JVM退出时自动删除（双重保险）
        tempFile.deleteOnExit();
        return tempFile;
    }

    /**
     * 3. 上传临时文件到OSS
     */
    private PutObjectResult uploadToOss(String uploadPath, File tempFile) throws IOException {
        // 初始化OSS客户端
        OSS ossClient = new OSSClientBuilder().build(
                aliOssUtil.getEndpoint(),
                aliOssUtil.getAccessKeyId(),
                aliOssUtil.getAccessKeySecret()
        );

        try {
            // 准备文件元数据
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(tempFile.length());
            metadata.setContentType(getContentTypeBySuffix(FileUtil.getSuffix(tempFile.getName())));

            // 执行上传
            PutObjectRequest putObjectRequest = new PutObjectRequest(
                    aliOssUtil.getBucketName(),
                    uploadPath,
                    tempFile
            );
            putObjectRequest.setMetadata(metadata);
            return ossClient.putObject(putObjectRequest);
        } finally {
            // 关闭OSS客户端（资源清理）
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 4. 获取图片宽高信息（替代CI的ImageInfo）
     */
    private BufferedImage getImageDimension(File tempFile) throws IOException {
        // 通过ImageIO读取图片宽高（无CI时的兼容方案，与原有代码逻辑一致）
        try {
            return ImageIO.read(tempFile);
        } catch (IOException e) {
            log.warn("获取图片宽高失败，文件：{}", tempFile.getAbsolutePath(), e);
            return null;
        }
    }

    /**
     * 5. 构建上传结果对象（与PictureUploadTemplate的buildResult风格一致）
     */
    private UploadPictureResult buildUploadResult(String originFilename, File tempFile, String uploadPath, BufferedImage imageInfo) {
        UploadPictureResult result = new UploadPictureResult();
        // 原始文件名（不含扩展名）
        result.setPicName(FileUtil.mainName(originFilename));
        // 图片大小（字节）
        result.setPicSize(tempFile.length());
        // 图片格式（大写，如PNG、JPG）
        result.setPicFormat(FileUtil.getSuffix(originFilename).toUpperCase());
        // 图片宽高（若获取失败则设为0）
        if (imageInfo != null) {
            result.setPicWidth(imageInfo.getWidth());
            result.setPicHeight(imageInfo.getHeight());
            // 宽高比例（保留2位小数，避免除以零）
            if (imageInfo.getHeight() > 0) {
                double scale = Math.round((imageInfo.getWidth() * 1.0 / imageInfo.getHeight()) * 100) / 100.0;
                result.setPicScale(scale);
            }
        } else {
            result.setPicWidth(0);
            result.setPicHeight(0);
            result.setPicScale(0.0);
        }
        // OSS永久访问URL（格式：https://桶名.端点/上传路径）
        String endpoint = aliOssUtil.getEndpoint().replaceAll("^https?://", "");
        String accessUrl = String.format("https://%s.%s/%s", aliOssUtil.getBucketName(), endpoint, uploadPath);
        result.setUrl(accessUrl);

        return result;
    }

    /**
     * 6. 删除临时文件（与PictureUploadTemplate的deleteTempFile逻辑一致）
     */
    public void deleteTempFile(File file) {
        if (file == null || !file.exists()) {
            return;
        }
        boolean deleteSuccess = file.delete();
        if (!deleteSuccess) {
            log.error("临时文件删除失败，路径：{}", file.getAbsolutePath());
        }
    }

    /**
     * 7. 根据文件扩展名获取Content-Type（辅助方法）
     */
    private String getContentTypeBySuffix(String suffix) {
        if (StringUtils.isEmpty(suffix)) {
            return "application/octet-stream";
        }
        switch (suffix.toLowerCase()) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "webp":
                return "image/webp";
            case "bmp":
                return "image/bmp";
            default:
                return "application/octet-stream";
        }
    }


    // -------------------------- 差异化流程：子类必须实现 --------------------------
    /**
     * 1. 验证输入源有效性（如MultipartFile非空、URL格式合法）
     */
    protected abstract void validPicture(Object inputSource);

    /**
     * 2. 提取原始文件名（从输入源中获取，如MultipartFile的原始名、URL中的文件名）
     */
    protected abstract String getOriginFilename(Object inputSource);

    /**
     * 3. 处理文件（将输入源转换为临时文件，如MultipartFile写入临时文件、URL下载到临时文件）
     */
    protected abstract void processFile(Object inputSource, File tempFile) throws Exception;
}